package cn.mjnxin.iot.base.domain;

import cn.mjnxin.iot.base.constant.ConstantValue;
import cn.mjnxin.iot.base.util.MixUtil;

/**
 * 网关包头结构：
 * FLAG    2字节头部标识FAFB
 * DATALEN 4字节业务数据长度
 * TYPE    1字节类型  是否IPV4+7位标识
 * PID     2字节协议编号
 * GATENUM 1字节网关编号
 * IPADDR  ipv4:4/ipv6:16字节左侧网络地址
 * PORT    2字节左侧网络端口
 * COUNT   2字节序号,根据网络地址+端口分组
 * 
 * @author CLG 2023年10月10日
 * @version V1.0.0
 */
public class GatePacketWriter {
    private final static int INCREASE_DATA_SIZE = ConstantValue.GATE_HEAD_LENGTH;
    
    private boolean   inBigEndian  = false;// 默认小端
    private byte[]    dataBuffer;   // 真实存放数据的地方
    private int       pos          = 0; // 自定义指针即对应数组下标

    /**
     * 默认构造
     */
    public GatePacketWriter() {
        this(INCREASE_DATA_SIZE);
    }

    /**
     * 指定初始化字节数组大小
     * 
     * @param size
     */
    public GatePacketWriter(int size) {
        this.dataBuffer = new byte[size];
    }

    /**
     * 写入字节数组
     * @param data
     * @throws Exception
     */
    public void writeBytes(byte[] data) throws Exception {
        ensureCapacity(this.pos + data.length);
        for (int i = 0; i < data.length; i++) {
            this.dataBuffer[this.pos++] = data[i];
        }
    }

    /**
     * 写入一个字节
     * @param data
     * @throws Exception
     */
    public void writeInt8(byte data) throws Exception {
        ensureCapacity(this.pos + 1);
        this.dataBuffer[this.pos] = data;
        this.pos += 1;
    }

    /**
     * 写入端整数字节
     * @param i
     * @throws Exception
     */
    public void writeInt16(int i) throws Exception {
        ensureCapacity(this.pos + 2);
        if (this.inBigEndian) {
            MixUtil.short2byte(i, this.dataBuffer, this.pos);
        } else {
            MixUtil.Sshort2byte(i, this.dataBuffer, this.pos);
        }
        this.pos += 2;
    }

    /**
     * int
     * 
     * @param i
     * @throws Exception
     */
    public void writeInt24(int i) throws Exception {
        ensureCapacity(this.pos + 3);
        if (this.inBigEndian) {
            this.dataBuffer[this.pos++] = (byte) (i >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (i >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (i & 0xFF);
        } else {
            this.dataBuffer[this.pos++] = (byte) (i & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (i >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (i >>> 16 & 0xFF);
        }
    }

    /**
     * int
     * 
     * @param i
     * @throws Exception
     */
    public void writeInt32(int i) throws Exception {
        ensureCapacity(this.pos + 4);
        if (this.inBigEndian) {
            MixUtil.int2byte(i, this.dataBuffer, this.pos);
        } else {
            MixUtil.Sint2byte(i, this.dataBuffer, this.pos);
        }
        this.pos += 4;
    }

    /**
     * long
     * 
     * @param l
     * @throws Exception
     */
    public void writeInt40(long l) throws Exception {
        ensureCapacity(this.pos + 5);
        if (this.inBigEndian) {
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
        } else {
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
        }
    }

    /**
     * long
     * 
     * @param l
     * @throws Exception
     */
    public void writeInt48(long l) throws Exception {
        ensureCapacity(this.pos + 6);
        if (this.inBigEndian) {
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 40 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
        } else {
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 40 & 0xFF);
        }
    }

    /**
     * long
     * 
     * @param l
     * @throws Exception
     */
    public void writeInt56(long l) throws Exception {
        ensureCapacity(this.pos + 7);
        if (this.inBigEndian) {
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 48 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 40 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
        } else {
            this.dataBuffer[this.pos++] = (byte) (int) (l & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 8 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 16 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 24 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 32 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 40 & 0xFF);
            this.dataBuffer[this.pos++] = (byte) (int) (l >>> 48 & 0xFF);
        }
    }

    /**
     * long
     * 
     * @param l
     * @throws Exception
     */
    public void writeInt64(long l) throws Exception {
        ensureCapacity(this.pos + 8);
        if (this.inBigEndian) {
            MixUtil.long2byte(l, this.dataBuffer, this.pos);
        } else {
            MixUtil.Slong2byte(l, this.dataBuffer, this.pos);
        }
        this.pos += 8;
    }

    /**
     * 当字节数组不够用时以指定单位扩容
     * 
     * @param minCapacity
     * @throws Exception
     */
    public void ensureCapacity(int minCapacity) throws Exception {
        if (this.dataBuffer.length < minCapacity) {
            int nextBufSize = INCREASE_DATA_SIZE * (minCapacity / INCREASE_DATA_SIZE + 1);
            byte[] data = new byte[nextBufSize];
            System.arraycopy(this.dataBuffer, 0, data, 0, this.dataBuffer.length);
            this.dataBuffer = data;
        }
    }

    public boolean isInBigEndian() {
        return inBigEndian;
    }

    public void setInBigEndian(boolean inBigEndian) {
        this.inBigEndian = inBigEndian;
    }

    public byte[] getDataBuffer() {
        return dataBuffer;
    }

    public void setDataBuffer(byte[] dataBuffer) {
        this.dataBuffer = dataBuffer;
    }

    public int getPos() {
        return pos;
    }

    public void setPos(int pos) {
        this.pos = pos;
    }
}
